﻿using System;
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using EnvDTE80;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using Microsoft.VisualStudio.Shell.Interop;
using System.Threading.Tasks;

namespace QuickShare.Vsix.SqlList.Commons
{
    public static class DTEExtentions
    {
        public static async Task<IEnumerable<string>> GetSelectedItemPaths(this DTE2 dte)
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
            var items = (Array)dte.ToolWindows.SolutionExplorer.SelectedItems;
            var enumer = new List<string>();
            foreach(UIHierarchyItem selItem in items)
            {
                var item = selItem.Object as ProjectItem;
                if(item != null && item.Properties != null)
                    enumer.Add(item.Properties.Item("FullPath").Value.ToString());
            }
            return enumer;
        }

        public static async Task<IEnumerable<string>> GetSelectedFilePaths(this DTE2 dte)
        {
            var list = await GetSelectedItemPaths(dte);
            var rst = list.SelectMany(p => Directory.Exists(p) ? Directory.EnumerateFiles(p, "*", SearchOption.AllDirectories) : new[] { p });
            return rst;
        }

        public static async Task<IEnumerable<string>> GetProjectPaths(this DTE2 dte)
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
            var enumer = new List<string>();
            var projects = await dte.GetProjects();
            foreach(var proj in projects)
            {
                enumer.Add(Path.GetDirectoryName(proj.FullName));
            }
            return enumer;
        }
        public static async Task<IEnumerable<Project>> GetProjects(this DTE2 dte)
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
            var enumer = new List<Project>();
            var projects = dte.Solution.Projects;
            foreach(Project proj in projects)
            {
                var subs = await GetSubProjects(proj);
                foreach(var item in subs)
                {
                    enumer.Add(item);
                }
            }
            return enumer;
        }
        public static async Task<IEnumerable<Project>> GetSubProjects(ProjectItems projectItems)
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
            var enumer = new List<Project>();
            foreach(ProjectItem pitem in projectItems)
            {
                if(pitem.SubProject != null)
                {
                    var subs = await GetSubProjects(pitem.SubProject);
                    foreach(var item in subs)
                    {
                        enumer.Add(item);
                    }
                }
            }
            return enumer;
        }
        private static async Task<IEnumerable<Project>> GetSubProjects(Project proj)
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
            var enumer = new List<Project>();
            if(!string.IsNullOrWhiteSpace(proj.FileName) && proj.FileName.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase))
            {
                enumer.Add(proj);
            }
            else
            {
                var projs = await GetSubProjects(proj.ProjectItems);
                foreach(var item in projs)
                {
                    enumer.Add(item);
                }
            }
            return enumer;
        }

        public static async Task<Project> GetCurrentProject(this DTE2 dte)
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
            var items = await GetSelectedItemPaths(dte);
            var item = items.FirstOrDefault();
            if(item != null)
            {
                var projs = await dte.GetProjects();
                foreach(var proj in projs)
                {
                    var dir = Path.GetDirectoryName(proj.FullName);
                    if(item.StartsWith(dir + "\\"))
                    {
                        return proj;
                    }
                }
            }
            return null;
        }

        private static bool hasSqlListConsolePane = false;
        public static async System.Threading.Tasks.Task WriteInConsole(this DTE2 dte, string message)
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
            OutputWindow ow = dte.ToolWindows.OutputWindow;

            if(!string.IsNullOrWhiteSpace(message))
            {
                ////获取输出窗口中的窗格
                OutputWindowPane owP = null;
                if(hasSqlListConsolePane)
                {
                    owP = ow.OutputWindowPanes.Item("SqlList");
                }
                else
                {
                    owP = ow.OutputWindowPanes.Add("SqlList");
                    hasSqlListConsolePane = true;
                }
                owP.Activate();
                //输入信息
                owP.OutputString($"{DateTime.Now:yyyy-MM-dd HH:mm:ss:fff} {message.Trim()}{Environment.NewLine}");
            }
        }

        public static void ShowMessage(this AsyncPackage package, string message, string title)
        {
            if(string.IsNullOrWhiteSpace(message))
                return;
            VsShellUtilities.ShowMessageBox(
                   package,
                   message,
                   title,
                   OLEMSGICON.OLEMSGICON_INFO,
                   OLEMSGBUTTON.OLEMSGBUTTON_OK,
                   OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
        }

        /// <summary>
        /// 返回异常的具体信息，如果异常包含内部异常，会将内部异常的信息一起展示出来，
        /// 如果异常时聚合异常，会将内部所有异常的信息展示出来
        /// </summary>
        /// <param name="ex"></param>
        /// <returns></returns>
        public static string AllMessage(this Exception ex)
        {
            if(ex is AggregateException)
            {
                var aex = ex as AggregateException;
                var info = string.Join(Environment.NewLine, aex.InnerExceptions.Select(AllMessage).Distinct());
                return info;
            }
            var list = new List<string>();
            while(ex != null)
            {
                if(ex.InnerException == null)
                {
                    list.Add(ex.Message);
                }
                ex = ex.InnerException;
            }
            return string.Join(Environment.NewLine, list.Distinct());
        }
    }
}
